﻿// Decompiled with JetBrains decompiler
// Type: Netick.AdaptiveReplicator
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;
using System.Numerics;

#nullable disable
namespace Netick;

internal class AdaptiveReplicator
{
  internal List<Entity> SAPEntities;
  internal int LowestDivisor = 8;
  internal int[] Levels;
  internal int LevelsCount;
  private List<int> ChangedObjects;
  private int[] ChangedObjectsBool;
  private Netick.ClientReplicatorData[] ClientReplicatorData;
  private Vector3[] Positions;
  private int[] Divisors;
  private NetickEngine _engine;

  public AdaptiveReplicator(NetickEngine engine)
  {
    this._engine = engine;
    this.SAPEntities = new List<Entity>(engine.Config.MaxObjects);
    if (engine.IsServer)
    {
      this.ChangedObjects = new List<int>(engine.Config.MaxObjects);
      this.ChangedObjectsBool = new int[engine.Config.MaxObjects];
      this.Positions = new Vector3[engine.Config.MaxObjects];
      this.ClientReplicatorData = new Netick.ClientReplicatorData[engine.MaxClients];
    }
    this.Divisors = new int[engine.Config.MaxObjects * engine.MaxClients];
    this.GenerateRanges();
    for (int index = 0; index < this.Divisors.Length; ++index)
      this.Divisors[index] = 1;
  }

  private void GenerateRanges()
  {
    this.Levels = new int[6];
    for (int index = 0; index < this.Levels.Length; ++index)
      this.Levels[index] = (int) MathF.Pow(2f, (float) (index + 1));
    this.LevelsCount = (double) this._engine._config.TickRate < 100.0 ? ((double) this._engine._config.TickRate < 60.0 ? ((double) this._engine._config.TickRate < 30.0 ? ((double) this._engine._config.TickRate < 20.0 ? ((double) this._engine._config.TickRate < 10.0 ? 1 : 2) : 3) : 4) : 5) : 6;
    this.LowestDivisor = this.Levels[this.LevelsCount - 1];
  }

  internal void AddSnapshots()
  {
    RemoteInterpolation remoteInterpolation = this._engine._remoteInterpolation;
    if (remoteInterpolation._lastState == null || !this._engine._config.EnableSpatialPrioritization)
      return;
    Tick authoritativeTick = this._engine._cliSim.AuthoritativeTick;
    foreach (Entity receivedEntity in this._engine._cliSim.ReceivedEntities)
    {
      if (receivedEntity.UserEntity.UseSAP)
        receivedEntity.AdaptiveRemoteInterpolation?.AddSnapshot(authoritativeTick, remoteInterpolation._lastState, receivedEntity.Position);
    }
  }

  internal void AddEntity(Entity entity)
  {
    this.SAPEntities.Add(entity);
    this.Divisors[entity.RawId] = 1;
  }

  internal void RemoveEntity(Entity entity) => this.SAPEntities.RemoveUnordered(entity);

  public void Dirtify(int objId)
  {
    if (this.ChangedObjectsBool[objId] == 1)
      return;
    this.ChangedObjectsBool[objId] = 1;
    this.ChangedObjects.Add(objId);
  }

  internal void Update()
  {
    int count = this.ChangedObjects.Count;
    int maxClients = this._engine.MaxClients;
    int maxObjects = this._engine._config.MaxObjects;
    for (int index = 0; index < count; ++index)
    {
      int changedObject = this.ChangedObjects[index];
      Entity entity;
      if (this._engine.AllEntities.TryGetValue(changedObject, out entity))
        this.Positions[changedObject] = entity.Position;
    }
    for (int index = 0; index < maxClients; ++index)
      this.ClientReplicatorData[index] = new Netick.ClientReplicatorData()
      {
        Position = this._engine.Server.Clients[index].Position,
        ViewDirection = this._engine.Server.Clients[index].ViewDirection,
        Distance = this._engine.Server.Clients[index].Distance,
        FieldOfViewAngle = this._engine.Server.Clients[index].FieldOfViewAngle
      };
    for (int index1 = 0; index1 < count; ++index1)
    {
      int changedObject = this.ChangedObjects[index1];
      Vector3 position1 = this.Positions[changedObject];
      for (int index2 = 0; index2 < maxClients; ++index2)
      {
        Vector3 viewDirection = this.ClientReplicatorData[index2].ViewDirection;
        Vector3 position2 = this.ClientReplicatorData[index2].Position;
        float fieldOfViewAngle = this.ClientReplicatorData[index2].FieldOfViewAngle;
        float distance = this.ClientReplicatorData[index2].Distance;
        float num1 = Vector3.Distance(position1, position2);
        if ((double) num1 <= (double) distance)
        {
          this.Divisors[index2 * maxObjects + changedObject] = 1;
        }
        else
        {
          Vector3 vector3 = Vector3.op_Subtraction(position2, position1);
          double num2 = 180.0 / Math.PI * Math.Acos((double) Vector3.Dot(vector3, viewDirection) / ((double) vector3.Length() * (double) viewDirection.Length()));
          int num3 = this.LowestDivisor;
          double num4 = (double) fieldOfViewAngle;
          if (num2 > num4)
          {
            for (int index3 = 0; index3 < this.LevelsCount; ++index3)
            {
              if ((double) num1 <= (double) distance * (double) (index3 + 2))
              {
                num3 = this.Levels[index3];
                break;
              }
            }
          }
          this.Divisors[index2 * maxObjects + changedObject] = num3;
        }
      }
    }
    for (int index = 0; index < count; ++index)
      this.ChangedObjectsBool[this.ChangedObjects[index]] = 0;
    this.ChangedObjects.Clear();
  }

  public void Render()
  {
    int serverDivisor = this._engine._config.ServerDivisor;
    float rtt = (float) Math.Max((double) this._engine.FixedDeltaTime * 5.0, this._engine.Client.ConnectedServer.RTT.Average);
    Netick.ClientReplicatorData farRepData = this.GetFarRepData();
    Netick.ClientReplicatorData authCrd = this._engine.AuthoritativeTick.IsValid ? this._engine._cliSim._clientReplicatorDataBuffer[this._engine.AuthoritativeTick % this._engine._cliSim._clientReplicatorDataBuffer.Length] : new Netick.ClientReplicatorData();
    for (int index = 0; index < this.SAPEntities.Count; ++index)
      this.SAPEntities[index].AdaptiveRemoteInterpolation?.Update(serverDivisor, rtt, ref farRepData, ref authCrd, this);
  }

  internal Netick.ClientReplicatorData GetFarRepData()
  {
    Vector3 vector3_1 = this._engine.Tick.IsValid ? this._engine._cliSim._clientReplicatorDataBuffer[(this._engine.Tick - 1) % this._engine._cliSim._clientReplicatorDataBuffer.Length].Position : new Vector3();
    Vector3 vector3_2 = Vector3.op_Addition(vector3_1, Vector3.op_Multiply(10f, Vector3.op_Subtraction(vector3_1, this._engine.Tick.IsValid ? this._engine._cliSim._clientReplicatorDataBuffer[(this._engine.Tick - 0) % this._engine._cliSim._clientReplicatorDataBuffer.Length].Position : new Vector3())));
    return (this._engine.Tick.IsValid ? this._engine._cliSim._clientReplicatorDataBuffer[this._engine.Tick % this._engine._cliSim._clientReplicatorDataBuffer.Length] : new Netick.ClientReplicatorData()) with
    {
      Position = vector3_2
    };
  }

  internal int GetDivisorClient(Vector3 objPos, ref Netick.ClientReplicatorData crd)
  {
    float distance = crd.Distance;
    float num = Vector3.Distance(objPos, crd.Position);
    if ((double) num <= (double) distance)
      return 1;
    Vector3 vector3 = Vector3.op_Subtraction(crd.Position, objPos);
    if (180.0 / Math.PI * Math.Acos((double) Vector3.Dot(vector3, crd.ViewDirection) / ((double) vector3.Length() * (double) crd.ViewDirection.Length())) > (double) crd.FieldOfViewAngle)
    {
      for (int index = 0; index < this.LevelsCount; ++index)
      {
        if ((double) num <= (double) distance * (double) (index + 2))
          return this.Levels[index];
      }
    }
    return this.LowestDivisor;
  }

  public int GetDivisor(int cliIndex, int objId, int maxObjects)
  {
    return this.Divisors[cliIndex * maxObjects + objId];
  }
}
